#include "ConfigImpl.h"

#include "libxml/parser.h"
#include "libxml/tree.h"
#include "libxml/xmlsave.h"

namespace smartvoice {

/////////////////////////////////////////////////////////////////////////////

static bool LoadFromXMLDoc(KeyNodeImpl* key, xmlDocPtr doc);
static bool LoadFromXMLNode(KeyNodeImpl* key, xmlNodePtr node);
static bool FillXMLNode(const KeyNodeImpl* key, xmlNodePtr node);

static int OutputToOStream(void* context, const char* buffer, int len);

static void InitXmlErrorHandler();
static void DummyXmlErrorHandler(void*, const char* msg, ...);
static void LogXmlErrorIfAny(const char* description, ...);

/////////////////////////////////////////////////////////////////////////////

bool KeyNodeImpl::LoadFromMemory(const char* config)
{
	InitXmlErrorHandler();

	xmlDocPtr doc = xmlReadMemory(config, strlen(config), NULL, NULL, 0);
	if (doc == 0)
	{
		LogXmlErrorIfAny("Failed to parse memory-config: ");
		return false;
	}

	bool succeed = LoadFromXMLDoc(this, doc);
	xmlFreeDoc(doc);
	return succeed;
}

bool KeyNodeImpl::LoadFromFile(const char* file)
{
	InitXmlErrorHandler();

	xmlDocPtr doc = xmlReadFile(file, NULL, 0);
	if (doc == 0)
	{
		LogXmlErrorIfAny("Failed to parse %s: ", file);
		return false;
	}

	bool succeed = LoadFromXMLDoc(this, doc);
	xmlFreeDoc(doc);
	return succeed;
}

bool KeyNodeImpl::SaveToOStream(ostream &os) const
{
	InitXmlErrorHandler();

	xmlDocPtr doc = xmlNewDoc(NULL);
	if (doc == 0)
		return false;

	xmlNodePtr root = xmlNewDocNode(doc, NULL, reinterpret_cast<const xmlChar*>(GetName()), NULL);
	if (root == 0)
	{
		xmlFreeDoc(doc);
		return false;
	}
	xmlDocSetRootElement(doc, root);

	bool succeed = FillXMLNode(this, root);
	if (succeed)
	{
		xmlSaveCtxtPtr ctxt = xmlSaveToIO(OutputToOStream, NULL, &os, NULL, 0);
		succeed = (ctxt != NULL && xmlSaveDoc(ctxt, doc) >= 0 && xmlSaveFlush(ctxt) >= 0);
		if (ctxt != NULL)
			xmlSaveClose(ctxt);
		os.flush();
	}

	xmlFreeDoc(doc);
	return succeed;
}

/////////////////////////////////////////////////////////////////////////////

static bool LoadFromXMLDoc(KeyNodeImpl* key, xmlDocPtr doc)
{
	xmlNodePtr root = xmlDocGetRootElement(doc);
	return root != 0 && LoadFromXMLNode(key, root);
}

static bool LoadFromXMLNode(KeyNodeImpl* key, xmlNodePtr node)
{
	key->Clear();
	key->SetName(reinterpret_cast<const char*>(node->name));

	for (xmlNodePtr child = node->children; child != 0; child = child->next)
	{
		if (XML_ELEMENT_NODE != child->type)
			// the child is not an element, ignored
			continue;

		xmlNodePtr cf = child->children;
		xmlNodePtr cl = child->last;
		if (0 == cf)
			// the child has no children, ignored
			continue;
		if (cf == cl && XML_TEXT_NODE == cf->type)
		{
			// the child has only one child which is text node,
			// it should be a ValueNode

			ValueNodeImpl *node = new ValueNodeImpl(reinterpret_cast<const char*>(child->name));
			node->SetValue(reinterpret_cast<const char*>(cf->content));

			key->GetValues().push_back(node);
		}
		else
		{
			// otherwise the child should be a subkey node
			KeyNodeImpl *subkey = new KeyNodeImpl();
			key->GetSubkeys().push_back(subkey);
			if (!LoadFromXMLNode(subkey, child))
				return false;
		}
	}

	return true;
}

static bool FillXMLNode(const KeyNodeImpl* key, xmlNodePtr node)
{
	const KeyNodeImpl::ValueList &values = key->GetValues();
	for (KeyNodeImpl::ValueList::const_iterator iter = values.begin();
		 iter != values.end();
		 ++iter)
	{
		ValueNode *value = *iter;

		const xmlChar* sub_name  = reinterpret_cast<const xmlChar*>(value->GetName());
		const xmlChar* sub_value = reinterpret_cast<const xmlChar*>(value->GetValue());

		xmlNewTextChild(node, NULL, sub_name, sub_value);
	}

	const KeyNodeImpl::KeyNodeList &subkeys = key->GetSubkeys();
	for (KeyNodeImpl::KeyNodeList::const_iterator iter = subkeys.begin();
		 iter != subkeys.end();
		 ++iter)
	{
		KeyNodeImpl* sub_key = dynamic_cast<KeyNodeImpl*>(*iter);

		const xmlChar* sub_name  = reinterpret_cast<const xmlChar*>(sub_key->GetName());
		xmlNodePtr sub_node = xmlNewChild(node, NULL, sub_name, NULL);
		if (sub_node == 0 || !FillXMLNode(sub_key, sub_node))
			return false;
	}

	return true;
}

static int OutputToOStream(void* context, const char* buffer, int len)
{
	std::ostream &os = *reinterpret_cast<std::ostream *>(context);
	os.write(buffer, len);
	return len;
}

static void InitXmlErrorHandler()
{
	xmlGenericErrorFunc error_func = &DummyXmlErrorHandler;
	initGenericErrorDefaultFunc(&error_func);
}

static void DummyXmlErrorHandler(void*, const char* msg, ...)
{
	// nothing to do
}

static void LogXmlErrorIfAny(const char* description, ...)
{
	xmlErrorPtr error = xmlGetLastError();
	if (error != 0 && error->message != 0)
	{
		char buff[1024];
		va_list args;
		va_start(args, description);
		vsnprintf(buff, sizeof(buff), description, args);
		va_end(args);

		LOG(CFG_ERROR, "%s%s", buff, error->message);
	}

	if (error != 0)
		xmlResetError(error);
}

} // end namespace smartvoice

